#include <QCoreApplication>
#include <QProcess>
#include <QTimer>
#include <QFile>
#include <QStorageInfo>
#include <QRegularExpression>
#include <QDebug>

#include "setting/JSON_settings.h"
#include "coordinator/coordinator.h"

#include "surveillance.h"

#define PERIOD_CHECK		2000				// 2 seconds

Surveillance_Worker::Surveillance_Worker(QObject *parent)
    : QObject(parent)
{
    QSettings settings(JSONSettings::GetSettingFilePath(), JSONSettings::GetFormat(), QCoreApplication::instance());
    m_device1_name = settings.value(JSON_SURVEILLANCE_DEVICE1).toString();
    m_device2_name = settings.value(JSON_SURVEILLANCE_DEVICE2).toString();
    m_video_path = settings.value(JSON_SURVEILLANCE_PATH).toString();

    m_process = nullptr;
    m_camera1_online = m_camera2_online = false;

    freeSpace();
}

Surveillance_Worker::~Surveillance_Worker()
{
	if (m_process)
		stop();
}

void Surveillance_Worker::start()
{
	m_check_timer = new QTimer(this);
    connect(m_check_timer, &QTimer::timeout, this, &Surveillance_Worker::camera_check);
    m_check_timer->start(PERIOD_CHECK);
}

void Surveillance_Worker::stop()
{
	delete m_check_timer;
}

void Surveillance_Worker::start_video_record()
{
	QStringList arguments;
	QString filter_complex;

    qRegisterMetaType<QProcess::ExitStatus>("QProcess::ExitStatus");
    qRegisterMetaType<QProcess::ProcessState>("QProcess::ProcessState");
    qRegisterMetaType<QProcess::ProcessError>("QProcess::ProcessError");

	stop_video_record();

	if (!QFile::exists(m_device1_name) || !QFile::exists(m_device2_name)) {
		qInfo() << "[SURVEILLANCE]Video device not attached!";
		return;
	}
    freeSpace();

	m_start_time = QDateTime::currentDateTime();

	m_process = new QProcess();
	m_process->setProgram("ffmpeg");							// command name
	arguments << "-y";											// overwrite output file
	arguments << "-s" << "480x320";
	arguments << "-i" << m_device1_name;						// input device 0
	arguments << "-s" << "480x320";
	arguments << "-i" << m_device2_name;						// input device 1
																// compose complex filter:
	    filter_complex 	= "[0:v]rotate=PI/2:ow=ih:oh=iw[v0];";	// rotate 90 degree device 0's video => v0
	    filter_complex += "[1:v]rotate=PI/2:ow=ih:oh=iw[v1];";	// rotate 90 degree device 1's video => v1
	    filter_complex += "[v0] [v1] hstack=inputs=2 [v];";	// place v0, v1 side by side => v
        filter_complex += "[v]drawtext='";						// draw text on v
	    filter_complex += "fontfile=monofonto.ttf: fontsize=14: fontcolor=white: ";
	    																// assign text font, size, color
	    filter_complex += "box=1: boxcolor=black@0.5: boxborderw=5: ";
	    																// assign text-box border, color
	    filter_complex += "text=%{pts\\:localtime\\:" + QString("%1").arg(m_start_time.toSecsSinceEpoch()) + "}: ";
	    																// assign text content(presentation time-stamp(pts) start at current epoch)
	    filter_complex += "x=(w-tw)/2: y=10";							// assign text position
	    filter_complex += "'";										// end text filter
    arguments << "-filter_complex" <<  filter_complex;			// complex filter graph
    arguments << "-timestamp" << "now";							// add timestamp to output file
    arguments << m_video_path + "/surveillance.mp4";			// output file
	m_process->setArguments(arguments);
	m_process->setWorkingDirectory(m_video_path);

    connect(m_process, &QProcess::errorOccurred, this, &Surveillance_Worker::errorOccurred);
    connect(m_process, &QProcess::started, this, &Surveillance_Worker::started);
    connect(m_process, &QProcess::stateChanged, this, &Surveillance_Worker::stateChanged);
    connect(m_process, &QProcess::readyReadStandardError, this, &Surveillance_Worker::readyReadStandardError);
    connect(m_process, &QProcess::readyReadStandardOutput, this, &Surveillance_Worker::readyReadStandardOutput);
    connect(m_process, QOverload<int, QProcess::ExitStatus>::of(&QProcess::finished), this, &Surveillance_Worker::finished);

    qInfo() << "[SURVEILLANCE]Start video recording";

    m_process->start();
}

void Surveillance_Worker::stop_video_record()
{
	QString file_name;

	m_stop_time = QDateTime::currentDateTime();
	if (m_process) {
        qInfo() << "[SURVEILLANCE]Stop video recording";

		m_process->terminate();
        if (!m_process->waitForFinished(10 * 1000)) {
            qInfo() << "[SURVEILLANCE]Kill the process directly!";
            m_process->kill();
        }
		m_process->deleteLater();

		file_name = m_start_time.toString("yyyyMMdd.hhmmss") + "-" + m_stop_time.toString("yyyyMMdd.hhmmss");
        qInfo() << "Rename" << m_video_path + "/surveillance.mp4" << "to" << m_video_path + "/" + file_name + ".mp4"
        		<< QFile::rename(m_video_path + "/surveillance.mp4", m_video_path + "/" + file_name + ".mp4");
	}
	m_process = nullptr;
}

void Surveillance_Worker::camera_check()
{

}

void Surveillance_Worker::errorOccurred(QProcess::ProcessError error)
{
	qInfo() << "[SURVEILLANCE]Error:" << error;
}

void Surveillance_Worker::finished(int exitCode, QProcess::ExitStatus exitStatus)
{
    qInfo() << "[SURVEILLANCE]Finished" << exitCode << exitStatus;
}

void Surveillance_Worker::readyReadStandardError()
{
//    static QRegularExpression regExp = QRegularExpression("^.*time=([0-9]{2}):([0-9]{2}):([0-9]{2}).*$");
//    QString msg = m_process->readAllStandardError();
//    QRegularExpressionMatch match = regExp.match(msg);
//    if (match.hasMatch())
//        qInfo().noquote() << QString("[SURVEILLANCE]Time: %1:%2:%3").arg(match.captured(1), match.captured(2), match.captured(3));
}

void Surveillance_Worker::readyReadStandardOutput()
{
    qInfo() << m_process->readAllStandardOutput();
}

void Surveillance_Worker::started()
{
	qInfo() << "[SURVEILLANCE]Process Started";
}

void Surveillance_Worker::stateChanged(QProcess::ProcessState newState)
{
	qInfo() << "[SURVEILLANCE]State:" << newState;
}

void Surveillance_Worker::freeSpace(void)
{
	qint64 free_bytes, wanted_free_bytes;
	QDir dir(m_video_path);

	free_bytes = QStorageInfo(m_video_path).bytesFree();
	wanted_free_bytes = 2L * 1000L * 1000L * 1000L;
	if (free_bytes > wanted_free_bytes)
		return;
	qInfo() << "[SURVEILLANCE]Free Space:" << QString("%L1").arg(free_bytes);

	QFileInfoList entries = dir.entryInfoList(QStringList() << "*.mp4", QDir::Files, QDir::Time | QDir::Reversed);
	foreach(QFileInfo entry, entries) {
		if (free_bytes < wanted_free_bytes) {
			qInfo() << "[SURVEILLANCE]Delete file:" << entry.filePath() << wanted_free_bytes - free_bytes;
			QFile::remove(entry.filePath());
			free_bytes += entry.size();
		}
	}
}

SurveillanceController::SurveillanceController(QObject *parent)
	: m_worker_thread()
{
	m_coordinator = qobject_cast<Coordinator *>(parent);

    m_worker = new Surveillance_Worker();
	m_worker->moveToThread(&m_worker_thread);

	connect(&m_worker_thread, &QThread::finished, m_worker, &QObject::deleteLater);
    connect(this, &SurveillanceController::start, m_worker, &Surveillance_Worker::start);
    connect(this, &SurveillanceController::stop, m_worker, &Surveillance_Worker::stop);

    m_worker_thread.setObjectName("Surveillance Thread");
	m_worker_thread.start();
}

SurveillanceController::~SurveillanceController()
{
	m_worker_thread.quit();
	m_worker_thread.wait();
}

